package com.lkd.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import com.lkd.common.UserConstants;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.SupplyChannel;
import com.lkd.contract.SupplyContract;
import com.lkd.contract.TaskCompleteContract;
import com.lkd.dao.TaskDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.TaskDetailsEntity;
import com.lkd.entity.TaskEntity;
import com.lkd.entity.TaskStatusTypeEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.http.vo.CancelTaskViewModel;
import com.lkd.http.vo.TaskDetailsViewModel;
import com.lkd.http.vo.TaskViewModel;
import com.lkd.service.TaskDetailsService;
import com.lkd.service.TaskService;
import com.lkd.service.TaskStatusTypeService;
import com.lkd.vo.Pager;
import com.lkd.vo.UserVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TaskServiceImpl extends ServiceImpl<TaskDao,TaskEntity> implements TaskService{

    @Autowired
    private TaskStatusTypeService statusTypeService;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private VMService vmService;

    @Autowired
    private UserService userService;

    @Autowired
    private TaskDetailsService taskDetailsService;

    @Autowired
    private MqttProducer producer;


    @Override
    public Pager<TaskEntity> search(Long pageIndex, Long pageSize, String innerCode, Integer userId, String taskCode, Integer status, Boolean isRepair, String start, String end) {

        Page<TaskEntity> page = new Page<>(pageIndex,pageSize);
        LambdaQueryWrapper<TaskEntity> qw = new LambdaQueryWrapper<>();
        if(!Strings.isNullOrEmpty(innerCode)){
            qw.eq(TaskEntity::getInnerCode,innerCode);
        }
        if(userId != null && userId > 0){
            qw.eq(TaskEntity::getUserId,userId);
        }
        if(!Strings.isNullOrEmpty(taskCode)){
            qw.like(TaskEntity::getTaskCode,taskCode);
        }
        if(status != null && status > 0){
            qw.eq(TaskEntity::getTaskStatus,status);
        }
        if(isRepair != null){
            if(isRepair){
                qw.ne(TaskEntity::getProductTypeId, VMSystem.TASK_TYPE_SUPPLY);
            }else {
                qw.eq(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
            }
        }
        if(!Strings.isNullOrEmpty(start) && !Strings.isNullOrEmpty(end)){
            qw
                    .ge(TaskEntity::getCreateTime, LocalDate.parse(start, DateTimeFormatter.ISO_LOCAL_DATE))
                    .le(TaskEntity::getCreateTime,LocalDate.parse(end,DateTimeFormatter.ISO_LOCAL_DATE));
        }
        //根据最后更新时间倒序排序
        qw.orderByDesc(TaskEntity::getUpdateTime);

        return Pager.build(this.page(page,qw));
    }



    @Override
    public List<TaskStatusTypeEntity> getAllStatus() {
        QueryWrapper<TaskStatusTypeEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .ge(TaskStatusTypeEntity::getStatusId,VMSystem.TASK_STATUS_CREATE);

        return statusTypeService.list(qw);
    }

    /**
     * 查询未完成的工单
     * @param innerCode   售货机编码
     * @param productType  工单类型
     * @return true 有未完成的工单 false没有
     */
    private boolean checkTodoTask(String innerCode,int productType){
        int count = count(Wrappers.<TaskEntity>lambdaQuery()
                .eq(TaskEntity::getInnerCode, innerCode)
                .eq(TaskEntity::getProductTypeId, productType)
                // .in(TaskEntity::getTaskStatus,VMSystem.TASK_STATUS_CREATE,VMSystem.TASK_STATUS_PROGRESS)
                .le(TaskEntity::getTaskStatus, VMSystem.TASK_STATUS_PROGRESS));

       return count > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class) //rollbackFor Exception所有异常都会进行回滚，如果不加只会对checked异常回滚（extends Exception）
    public void create(TaskViewModel taskViewModel) {
        String innerCode = taskViewModel.getInnerCode();
        int productType = taskViewModel.getProductType();

        //判断 同一台设备  同种工单类型  有没有未完成的工单
        if(checkTodoTask(innerCode, productType)){
            throw new LogicException("存在相同类型的未完成订单");
        }

        //远程调用 售货机微服务
        VmVO vmInfo = vmService.getVMInfo(innerCode);
        //校验售货机是否存在
        if(vmInfo == null){
            throw new LogicException("售货机编码不正确!");
        }

        //校验售货机状态
        Integer vmStatus = vmInfo.getVmStatus();
        if(vmStatus == null){
            throw new LogicException("售货机状态未知，请核实!");
        }
        checkVmStatus(productType,vmStatus);

        //创建数据库实体类对象
        TaskEntity taskEntity = new TaskEntity();
        //Bean复制，将参数对象赋值到数据库实体类对象
        BeanUtils.copyProperties(taskViewModel,taskEntity);
        //taskCode编码
        taskEntity.setTaskCode(generateTaskCode());
        //状态
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_CREATE);
        //区域ID
        taskEntity.setRegionId(vmInfo.getRegionId());
        //用户名
        UserVO user = userService.getUser(taskViewModel.getUserId());
        //校验用户是否存在
        if(user == null){
            throw new LogicException("执行人信息不存在!");
        }
        taskEntity.setUserName(user.getUserName());
        //TODO 手动赋值参数名和数据库字段名不一致
        taskEntity.setProductTypeId(taskViewModel.getProductType());
        //售货机地址
        taskEntity.setAddr(vmInfo.getNodeAddr());
        //保存到数据库
        save(taskEntity);

        //补货工单
        if(productType == VMSystem.TASK_TYPE_SUPPLY){
            //保存到details
            Long taskId = taskEntity.getTaskId();
            //将参数中的集合 转换成实体类对象 集合
            List<TaskDetailsViewModel> details = taskViewModel.getDetails();
            if(CollectionUtils.isEmpty(details)){
                //集合为空
                throw new LogicException("补货工单必须填写补货详情!");
            }
//            //1.循环转化
//            for (TaskDetailsViewModel detail : details) {
//                TaskDetailsEntity taskDetailsEntity = new TaskDetailsEntity();
//                taskDetailsEntity.setTaskId(taskId);
//                BeanUtils.copyProperties(detail,taskDetailsEntity);
//                //保存到数据库
//                taskDetailsService.save(taskDetailsEntity);
//            }
//
//            //2.批量写入
//            List<TaskDetailsEntity> taskDetailsEntities = new ArrayList<>(details.size());
//            for (TaskDetailsViewModel detail : details) {
//                TaskDetailsEntity taskDetailsEntity = new TaskDetailsEntity();
//                taskDetailsEntity.setTaskId(taskId);
//                BeanUtils.copyProperties(detail,taskDetailsEntity);
//                taskDetailsEntities.add(taskDetailsEntity);
//            }
//            //保存到数据库
//            taskDetailsService.saveBatch(taskDetailsEntities);

            //3.stream流
            List<TaskDetailsEntity> list = details.stream().map(data -> {
                TaskDetailsEntity taskDetailsEntity = new TaskDetailsEntity();
                taskDetailsEntity.setTaskId(taskId);
                BeanUtils.copyProperties(data, taskDetailsEntity);
                return taskDetailsEntity;
            }).collect(Collectors.toList());
            taskDetailsService.saveBatch(list);


        }

        //将工单数+1
        updateUserTaskCount(taskEntity.getRegionId(),taskEntity.getProductTypeId(),
                taskEntity.getUserId(),1);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void complete(TaskEntity task) {
        //判断工单状态不是待处理抛出异常
        if(Objects.equals(task.getTaskStatus(), VMSystem.TASK_STATUS_CANCEL)
                || Objects.equals(task.getTaskStatus(), VMSystem.TASK_STATUS_FINISH)){
            throw new LogicException("工单状态不正确!");
        }

        task.setTaskStatus(VMSystem.TASK_STATUS_FINISH); //修改状态为已完成
        updateById(task);

        //完成工单时，如果是投放工单，将售货机状态更改为运营
        //撤机工单，将售货机状态更改为撤机
        operationMsgSend(task);

        //补货工单消息发送
        supplyMsgSend(task);

        //将工单数-1
        updateUserTaskCount(task.getRegionId(),task.getProductTypeId(),
                task.getUserId(),-1);
    }

    @Override
    public void cancel(TaskEntity task,CancelTaskViewModel model) {
        //工单状态不正确
        if(Objects.equals(task.getTaskStatus(), VMSystem.TASK_STATUS_CANCEL)
                || Objects.equals(task.getTaskStatus(), VMSystem.TASK_STATUS_FINISH)){
            throw new LogicException("工单状态不正确!");
        }
        task.setTaskStatus(VMSystem.TASK_STATUS_CANCEL); //修改状态为已经取消
        task.setDesc(model.getDesc());
        updateById(task);

        //将工单数-1
        updateUserTaskCount(task.getRegionId(),task.getProductTypeId(),
                task.getUserId(),-1);
    }



    private void supplyMsgSend(TaskEntity task){
        if(Objects.equals(task.getProductTypeId(),VMSystem.TASK_TYPE_SUPPLY)){
            //创建封装消息对象
            SupplyContract supplyContract = new SupplyContract();
            supplyContract.setInnerCode(task.getInnerCode());
            //补货详情数据
            List<TaskDetailsEntity> list = taskDetailsService.getByTaskId(task.getTaskId());
            //判断集合是否为空
            if(CollectionUtils.isEmpty(list)){
                throw new LogicException("工单详情不存在!");
            }

            List<SupplyChannel> supplyData = list.stream().map(data -> {
                SupplyChannel supplyChannel = new SupplyChannel();
                supplyChannel.setChannelId(data.getChannelCode()); //字段名字取的不好
                supplyChannel.setCapacity(data.getExpectCapacity());
                return supplyChannel;
            }).collect(Collectors.toList());
            supplyContract.setSupplyData(supplyData);

            try {
                producer.send(TopicConfig.VMS_SUPPLY_TOPIC,2,supplyContract);
            } catch (JsonProcessingException e) {
                log.error("完成工单消息转换json出错",e);
                throw new LogicException("完成工单数据异常!");
            }
        }
    }

    private void operationMsgSend(TaskEntity task) {
        if(Objects.equals(task.getProductTypeId(), VMSystem.TASK_TYPE_DEPLOY)
            || Objects.equals(task.getProductTypeId(), VMSystem.TASK_TYPE_REVOKE)
        ){
            TaskCompleteContract contract = new TaskCompleteContract();
            contract.setTaskType(task.getProductTypeId()); //工单类型
            contract.setInnerCode(task.getInnerCode());

            try {
                producer.send(TopicConfig.VMS_COMPLETED_TOPIC,2,contract);
            } catch (JsonProcessingException e) {
                log.error("完成工单消息转换json出错",e);
                throw new LogicException("完成工单数据异常!");
            }
        }
    }

    private void checkVmStatus(int productType, Integer status) {
        if(productType == VMSystem.TASK_TYPE_DEPLOY &&
                status.equals(VMSystem.VM_STATUS_RUNNING) ){
            throw new LogicException("运营中的售货机无法创建投放工单!");
        }
        if(productType == VMSystem.TASK_TYPE_SUPPLY &&
                !status.equals(VMSystem.VM_STATUS_RUNNING) ){
            throw new LogicException("非运营中的售货机无法创建补货工单!");
        }
        if(productType == VMSystem.TASK_TYPE_REVOKE &&
                !status.equals(VMSystem.VM_STATUS_RUNNING) ){
            throw new LogicException("非运营中的售货机无法创建撤机工单!");
        }
    }


    /**
     * 生成工单编号 分析代码
     * @return
     */
    private String generateTaskCode(){
        //日期+序号
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));  //日期字符串
        String key= "lkd.task.code."+date; //redis key
        Object obj = redisTemplate.opsForValue().get(key);
        if(obj==null){
            redisTemplate.opsForValue().set(key,1L, Duration.ofDays(1) );
            return date+"0001";
        }
        return date+  Strings.padStart( redisTemplate.opsForValue().increment(key,1).toString(),4,'0');
    }


    /**
     * 更新redis中的分值工单数
     * @param regionId 区域ID
     * @param taskType 工单类型
     * @param userId    用户ID
     * @param value     分值增加或者减少
     */
    private void updateUserTaskCount(long regionId,int taskType,long userId,double value){
        //1.拼接zset key
        String key = VMSystem.getUserTaskCountRedisKey(regionId, VMSystem.getRoleCodeByTaskType(taskType));
        //2.更新zset数据
        redisTemplate.opsForZSet().incrementScore(key,userId,value);
    }

    @Override
    public Integer getLeastTaskCountUser(long regionId, boolean repair) {
        String key = VMSystem.getUserTaskCountRedisKey(regionId,
                repair? UserConstants.ROLE_CODE_REPAIRER:UserConstants.ROLE_CODE_OPERATOR
                );
        Set<Object> range = redisTemplate.opsForZSet().range(key, 0, 0);
        return (Integer)(new ArrayList<>(range).get(0));
    }
}
